package com.info.springmongo.service.impl;

import com.info.springmongo.entity.Student;
import com.info.springmongo.service.StudentService;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.GroupOperation;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Service
public class StudentServiceImpl implements StudentService {

    private final MongoTemplate mongoTemplate;

    private AtomicLong currentId = new AtomicLong(0);

    public StudentServiceImpl(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @PostConstruct
    public void loadCurrentId() {
        GroupOperation operation = Aggregation.group("id").last("id").as("maxId");
        TypedAggregation<Student> aggregation = Aggregation.newAggregation(Student.class, operation);
        Long maxId = mongoTemplate.aggregate(aggregation, Student.class)
                .getMappedResults()
                .stream()
                .max(Comparator.comparing(Student::getId))
                .map(Student::getId).orElse(0L);
        currentId = new AtomicLong(new AtomicLong(maxId).incrementAndGet());
    }

    @Override
    public int save(Student student) {
        student.setTimeOfEnrollment(LocalDateTime.now());
        student.setId(currentId.getAndIncrement());
        mongoTemplate.insert(student);
        return 1;
    }

    @Override
    public int update(Student student) {
        Query query = new Query(Criteria.where("_id").is(student.getId()));
        Update update = new Update().set("name", student.getName());
        try {
            mongoTemplate.updateFirst(query, update, Student.class);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    @Override
    public Student getById(Long id) {
        Query query = new Query(Criteria.where("_id").is(id));
        return mongoTemplate.findOne(query, Student.class);
    }

    @Override
    public List<Student> getList(Student student) {
        if (student == null) {
            return mongoTemplate.findAll(Student.class)
                    .stream()
                    .limit(10)
                    .collect(Collectors.toList());
        }
        Query query = new Query();
        if (student.getId() != null) {
            query.addCriteria(Criteria.where("_id").is(student.getId()));
        }
        if (!StringUtils.isEmpty(student.getName())) {
            query.addCriteria(Criteria.where("name").regex("^.*" + student.getName() + ".$"));
        }
        if (student.getAge() != null) {
            query.addCriteria(Criteria.where("age").is(student.getAge()));
        }
        if (student.getTimeOfEnrollment() != null) {
            query.addCriteria(Criteria.where("timeOfEnrollment").lte(student.getTimeOfEnrollment()));
        }
        return mongoTemplate.find(query, Student.class)
                .stream()
                .limit(10)
                .collect(Collectors.toList());
    }

    @Override
    public List<Student> listByPage(Student student, int pageNum, int pageSize) {
        int beginPage = pageNum > 0 ? (pageNum - 1) * pageSize : 0;
        Query query = new Query();
        if (student.getId() != null) {
            query.addCriteria(Criteria.where("_id").is(student.getId()));
        }
        if (!StringUtils.isEmpty(student.getName())) {
            query.addCriteria(Criteria.where("name").regex("^.*" + student.getName() + ".$"));
        }
        if (student.getAge() != null) {
            query.addCriteria(Criteria.where("age").is(student.getAge()));
        }
        if (student.getTimeOfEnrollment() != null) {
            query.addCriteria(Criteria.where("timeOfEnrollment").lte(student.getTimeOfEnrollment()));
        }
        return mongoTemplate.find(query, Student.class)
                .stream()
                .skip(beginPage)
                .limit(pageSize)
                .collect(Collectors.toList());
    }

    @Override
    public int delete(Long id) {
        Query query = new Query(Criteria.where("_id").is(id));
        mongoTemplate.findAndRemove(query, Student.class);
        return 1;
    }
}
